import {
  DataTypes,
  EntityError,
  awaitTimeout,
  HttpResponse,
  RuntimeError,
  RuntimeModelError,
  mergeDefaultInLeft,
  debounceAndAsyncMerge,
} from '@ibiz-template/core';
import {
  Srfuf,
  ControlVO,
  exportData,
  ValueExUtil,
  GridService,
  CodeListItem,
  IExportColumn,
  ConfigService,
  IGridRowState,
  isValueChange,
  ScriptFactory,
  IConfigService,
  ControllerEvent,
  getDefaultValue,
  GridNotifyState,
  IGridController,
  handleAllSettled,
  MDCtrlLoadParams,
  ISearchGroupData,
  calcDeCodeNameById,
  IGridColumnProvider,
  MDControlController,
  GridColumnController,
  getGridColumnProvider,
  IApiGridColumnMapping,
  GridUAColumnController,
} from '@ibiz-template/runtime';
import {
  IDEGrid,
  IDEDataExport,
  IDEGridColumn,
  IDEGridGroupColumn,
} from '@ibiz/model-core';
import dayjs from 'dayjs';
import { clone } from 'ramda';
import { createUUID } from 'qx-util';
import { IColumnExState, IGridExEvent, IGridExState } from '../../interface';
import { GridRowState } from './grid-row.state';
import { GridFieldColumnController } from './grid-field-column.controller';
import { GridFieldEditColumnController } from './grid-field-edit-column.controller';

/**
 * 表格控制器
 *
 * @export
 * @class GridController
 * @extends {MDControlController<T, S, E>}
 * @implements {IGridController<T, S, E>}
 * @template T
 * @template S
 * @template E
 */
export class GridController<
    T extends IDEGrid = IDEGrid,
    S extends IGridExState = IGridExState,
    E extends IGridExEvent = IGridExEvent,
  >
  extends MDControlController<T, S, E>
  implements IGridController<T, S, E>
{
  /**
   * 表格部件服务
   *
   * @type {GridService}
   * @memberof GridController
   */
  declare service: GridService;

  /**
   * 自身事件
   *
   * @readonly
   * @protected
   * @type {ControllerEvent<IGridExEvent>}
   * @memberof GridController
   */
  protected get _evt(): ControllerEvent<IGridExEvent> {
    return this.evt;
  }

  /**
   * 单元格超出呈现模式
   *
   * @readonly
   * @type {('wrap' | 'ellipsis')}
   * @memberof GridController
   */
  get overflowMode(): 'wrap' | 'ellipsis' {
    if (this.controlParams.overflowmode) {
      return this.controlParams.overflowmode;
    }
    return ibiz.config.grid.overflowMode;
  }

  /**
   * 隐藏无值的单位
   *
   * @readonly
   * @type {boolean}
   * @memberof GridController
   */
  get emptyHiddenUnit(): boolean {
    if (this.controlParams.emptyhiddenunit) {
      return (
        this.controlParams.emptyhiddenunit === 'true' ||
        this.controlParams.emptyhiddenunit === true
      );
    }
    return ibiz.config.grid.emptyHiddenUnit;
  }

  /**
   * @description 行编辑模式
   * @readonly
   * @type {('cell' | 'row' | 'all')}
   * @memberof GridController
   */
  get editShowMode(): 'cell' | 'row' | 'all' {
    return 'cell';
  }

  /**
   * 行编辑保存模式
   *
   * @readonly
   * @type {('cell-blur' | 'auto' | 'manual')}
   * @memberof GridController
   */
  get editSaveMode(): 'cell-blur' | 'auto' | 'manual' {
    return 'manual';
  }

  /**
   * 表格保存错误处理模式
   *
   * @readonly
   * @type {('default' | 'reset')}
   * @memberof GridController
   */
  get saveErrorHandleMode(): 'default' | 'reset' {
    if (this.controlParams.saveerrorhandlemode) {
      return this.controlParams.saveerrorhandlemode;
    }
    return ibiz.config.grid.saveErrorHandleMode;
  }

  /**
   * 切换行编辑状态
   *
   * @param {GridRowState} row
   * @param {boolean} [editable]
   * @param {boolean} [isSave=true]
   * @return {*}  {Promise<void>}
   * @memberof GridController
   */
  async switchRowEdit(
    row: GridRowState,
    editable?: boolean,
    isSave: boolean = true,
  ): Promise<void> {}

  /**
   * 是否有配置宽度自适应列
   *
   * @type {boolean}
   * @memberof GridController
   */
  hasAdaptiveColumn: boolean = false;

  /**
   * 是否有多级表头
   *
   * @type {boolean}
   * @memberof GridController
   */
  isMultistageHeader: boolean = false;

  /**
   * 所有表格列控制器集合
   *
   * @type {{ [key: string]: GridColumnController }}
   * @memberof GridController
   */
  columns: { [key: string]: GridColumnController } = {};

  /**
   * 所有表格属性列的控制器
   *
   * @type {{ [key: string]: GridFieldColumnController }}
   * @memberof GridController
   */
  fieldColumns: { [key: string]: GridFieldColumnController } = {};

  /**
   * 所有表格操作列的控制器
   *
   * @type {{ [key: string]: GridUAColumnController }}
   * @memberof GridController
   */
  uaColumns: { [key: string]: GridUAColumnController } = {};

  /**
   * 所有表格编辑列的控制器
   *
   * @type {{ [key: string]: GridFieldEditColumnController }}
   * @memberof GridController
   */
  editColumns: { [key: string]: GridFieldEditColumnController } = {};

  /**
   * 表格列的适配器
   *
   * @type {{ [key: string]: IGridColumnProvider }}
   * @memberof GridController
   */
  providers: { [key: string]: IGridColumnProvider } = {};

  /**
   * 分组属性列控制器
   *
   * @type {GridFieldColumnController}
   * @memberof GridController
   */
  groupFieldColumn?: GridFieldColumnController;

  /**
   * 聚合行标题
   *
   * @type {string}
   * @memberof GridController
   */
  aggTitle: string = '合计';

  /**
   * 数据导出对象
   *
   * @type {(IDEDataExport | undefined)}
   * @memberof GridController
   */
  dataExport: IDEDataExport | undefined;

  /**
   * 数据导出参数
   *
   * @type {IData}
   * @memberof GridController
   */
  dataExportParam: IData = {};

  /**
   * 数据导出列
   *
   * @type {IExportColumn[]}
   * @memberof GridController
   */
  allExportColumns: IExportColumn[] = [];

  /**
   * 数据导出代码表
   *
   * @type {Map<string, readonly}
   * @memberof GridController
   */
  allExportCodelistMap: Map<string, readonly CodeListItem[]> = new Map();

  /**
   * 应用配置储存服务
   *
   * @protected
   * @type {IConfigService}
   * @memberof GridController
   */
  protected configService!: IConfigService;

  /**
   * 分组代码表项集合
   *
   * @type {Readonly<CodeListItem[]> | undefined}
   * @memberof GridController
   */
  get groupCodeListItems(): Readonly<CodeListItem[]> | undefined {
    return this.groupFieldColumn?.codeListItems;
  }

  /**
   * 是否启用表格聚合
   *
   * @readonly
   * @type {boolean}
   */
  get enableAgg(): boolean {
    return this.model.aggMode !== 'NONE';
  }

  /**
   * 允许使用行编辑
   *
   * @readonly
   * @type {boolean}
   */
  get allowRowEdit(): boolean {
    return !!this.model.enableRowEdit && this.state.rowEditOpen;
  }

  /**
   * 允许使用行编辑次序调整
   *
   * @readonly
   * @type {boolean}
   */
  get enableRowEditOrder(): boolean {
    return !!this.model.enableRowEditOrder;
  }

  /**
   * 初始化state的属性
   *
   */
  protected initState(): void {
    super.initState();
    const { hideHeader, enablePagingBar, singleSelect, noSort } = this.model;
    this.state.rows = [];
    this.state.columnFilter = {};
    this.state.noSort = noSort === true;
    this.state.singleSelect = singleSelect === true;
    this.state.columnStates = [];
    this.state.aggResult = {};
    this.state.isAutoGrid = false;
    this.state.rowEditOpen = false;
    this.state.hideHeader = hideHeader;
    this.state.enablePagingBar = enablePagingBar;
    this.state.simpleData = [];
    this.state.size = 50;
    this.state.pageSizes = [50, 100, 200, 300, 500];
    this.state.columnModelCach = false;
  }

  /**
   * 生命周期-创建完成
   *
   */
  protected async onCreated(): Promise<void> {
    await super.onCreated();
    if (this.controlParams.pagesizes) {
      try {
        const pagesizes = JSON.parse(this.controlParams.pagesizes);
        this.state.pageSizes = pagesizes;
        this.state.size = pagesizes[0];
      } catch (error) {
        ibiz.log.error(error);
      }
    }
    this.service = new GridService(this.model);
    this.configService = new ConfigService(
      this.model.appId!,
      'table',
      `${
        this.context.srfuserid
      }_${this.view.model.codeName?.toLowerCase()}_${this.model.codeName?.toLowerCase()}`,
    );
    // !数据变更通知防抖，且合并参数,如果有同一批触发不同行的值变更则该方法需要重写
    this.dataChangeNotify = debounceAndAsyncMerge(
      this.dataChangeNotify.bind(this),
      (arr1, arr2): [GridRowState, string[]] => {
        return [arr1[0], Array.from(new Set([...arr1[1], ...arr2[1]]))];
      },
      200,
    );
    await this.service.init(this.context);
    await this.initColumnStates();
    await this.initGridColumns();
    await this.initGroup();
    await this.initExportData();
  }

  /**
   * 初始化数据导出对象
   *
   * @protected
   * @return {*}  {Promise<void>}
   */
  protected async initExportData(): Promise<void> {
    if (this.model.dedataExportId) {
      this.dataExport = this.dataEntity.appDEDataExports?.find(dataExport => {
        return dataExport.id === this.model.dedataExportId;
      });
      if (this.dataExport) {
        this.allExportColumns = await this.findAllExportColumns(
          this.dataExport,
        );
      }
    }
    if (this.allExportColumns.length > 0) {
      this.allExportColumns.forEach((exportColumn: IExportColumn) => {
        if (exportColumn.codeListItems) {
          this.allExportCodelistMap.set(
            exportColumn.appDEFieldId!,
            exportColumn.codeListItems!,
          );
        }
      });
      this.dataExportParam.fields = this.allExportColumns.map(
        x => x.appDEFieldId,
      );
      this.dataExportParam.header = this.allExportColumns.map(x => x.caption);
    } else {
      Object.keys(this.fieldColumns).forEach((key: string) => {
        if (this.fieldColumns[key].codeList) {
          this.allExportCodelistMap.set(
            key,
            this.fieldColumns[key].codeListItems!,
          );
        }
      });
      const { degridColumns = [] } = this.model;
      // 隐藏列， 操作列不导出
      const exportColumns = degridColumns.filter(
        x =>
          !x.hiddenDataItem &&
          !x.hideDefault &&
          x.hideMode !== 2 &&
          x.columnType !== 'UAGRIDCOLUMN',
      );
      this.dataExportParam.fields = exportColumns.map(item => item.id);
      this.dataExportParam.header = exportColumns.map(item => item.caption);
    }
  }

  /**
   * 填充导出代码表
   *
   * @protected
   * @return {*}  {Promise<void>}
   */
  protected async fillExportCodelistMap(): Promise<void> {
    // 表格导出列已填充代码表
    if (this.model.dedataExportId) {
      return;
    }

    const fillCodeList = async (
      key: string,
      fieldColumnC: GridFieldColumnController,
    ): Promise<void> => {
      const dataItems = await fieldColumnC.loadCodeList();
      this.allExportCodelistMap.set(key, dataItems!);
    };

    const exportColumnsPromises: Promise<void>[] = [];
    for (const [key, codeListItems] of this.allExportCodelistMap) {
      if (!codeListItems) {
        const fieldColumn = this.fieldColumns[key];
        if (fieldColumn.codeListItems) {
          this.allExportCodelistMap.set(key, fieldColumn.codeListItems!);
        } else {
          exportColumnsPromises.push(fillCodeList(key, fieldColumn));
        }
      }
    }

    if (exportColumnsPromises.length > 0) {
      await Promise.all(exportColumnsPromises);
    }
  }

  /**
   * 初始化表格分组
   *
   * @protected
   * @return {*}  {Promise<void>}
   */
  protected async initGroup(): Promise<void> {
    if (!this.model.enableGroup) {
      return;
    }
    const { groupAppDEFieldId, groupCodeListId, groupMode } = this.model;
    this.groupFieldColumn = Object.values(this.fieldColumns).find(
      item => item.model.appDEFieldId === groupAppDEFieldId,
    );
    if (!this.groupFieldColumn) {
      throw new RuntimeModelError(
        this.model,
        ibiz.i18n.t('runtime.controller.control.grid.attributeColumns'),
      );
    }
    const groupFieldName = this.groupFieldColumn.model.id;

    // 找到分类属性列的位置，如果不在第一列调整到第一列去
    const index = this.state.columnStates.findIndex(
      item => item.key === this.groupFieldColumn!.model.codeName,
    );
    if (index !== -1 && index !== 0) {
      if (this.isMultistageHeader) {
        throw new RuntimeModelError(
          this.model,
          ibiz.i18n.t('runtime.controller.control.grid.configureFirstColumn', {
            groupFieldName,
          }),
        );
      }
      // 如果找到了元素，则将其从原来的位置删除
      const removeEls = this.state.columnStates.splice(index, 1);
      // 将元素插入到数组的第一位
      this.state.columnStates.unshift(...removeEls);
    }

    if (groupMode === 'CODELIST') {
      if (!groupCodeListId) {
        throw new RuntimeModelError(
          this.model,
          ibiz.i18n.t('runtime.controller.control.grid.requiresCodeTable'),
        );
      }
      if (this.groupFieldColumn.model.appCodeListId !== groupCodeListId) {
        throw new RuntimeModelError(
          this.model,
          ibiz.i18n.t('runtime.controller.control.grid.noMatchCodeTable', {
            groupFieldName,
          }),
        );
      }
    }
    this.calcColumnFixed();
  }

  /**
   * 获取请求过滤参数
   *
   * @param extraParams
   * @returns
   */
  async getFetchParams(extraParams: IParams = {}): Promise<IParams> {
    if (this.model.enableItemPrivilege === true) {
      extraParams.srfdataaccessaction = true;
    }
    Object.assign(extraParams, this.state.columnFilter);
    return super.getFetchParams(extraParams);
  }

  /**
   * 部件加载后处理
   *
   * @param args
   * @param items
   * @returns
   */
  async afterLoad(
    args: MDCtrlLoadParams,
    items: ControlVO[],
  ): Promise<ControlVO[]> {
    // 初始化加载时需重置选中数据
    await super.afterLoad(args, items);
    // 每次表格刷新时通知表格属性列,加载代码表,避免动态代码表更新不及时
    await handleAllSettled(
      Object.values(this.fieldColumns).map(async fieldColumn => {
        await fieldColumn.loadCodeList();
      }),
      false,
    );
    // 生成表格row对象
    const rows = items.map(item => {
      const row = new GridRowState(item, this);
      this.gridStateNotify(row, GridNotifyState.LOAD);
      return row;
    });
    if (args.isLoadMore) {
      this.state.rows.push(...rows);
    } else {
      this.state.rows = rows;
    }
    // 响应式写法，用state里遍历出来的row才是reactive
    await this.updateRows(this.state.rows);
    this.calcGroupData(this.state.items);
    await this.loadRemoteAgg();
    this.calcAggResult(this.state.items);
    return items;
  }

  /**
   * @description 处理刷新模式
   * @protected
   * @param {MDCtrlLoadParams} args
   * @memberof GridController
   */
  protected handleRefreshMode(args: MDCtrlLoadParams): void {
    super.handleRefreshMode(args);
    if (args.isInitialLoad || this.refreshMode === 'nocache')
      this.state.expandRowKeys = [];
  }

  /**
   * 更新行状态
   *
   * @param rows
   */
  async updateRows(rows: IGridRowState[]): Promise<void> {
    for (const row of rows) {
      await Promise.all([
        ...Object.values(row.uaColStates).map(uaState =>
          uaState.update(
            this.context,
            row.data.getOrigin(),
            this.model.appDataEntityId,
          ),
        ),
        ...Object.values(row.uiActionGroupStates).map(uaState =>
          uaState.update(
            this.context,
            row.data.getOrigin(),
            this.model.appDataEntityId,
          ),
        ),
      ]);
    }
  }

  /**
   * 计算分组数据
   *
   * @protected
   * @param {IData[]} items
   */
  protected calcGroupData(items: IData[]): void {
    const { enableGroup, groupMode } = this.model;
    if (enableGroup) {
      const groupMap = new Map<string | number, IData[]>();
      const codeListGroup = groupMode === 'CODELIST';
      const groupFiledName = this.groupFieldColumn!.model.id!;
      if (codeListGroup) {
        this.groupCodeListItems!.forEach(item => {
          groupMap.set(item.value, []);
        });
      }
      items.forEach(item => {
        const groupFieldValue = item[groupFiledName];
        // 非代码表分组时，不存在时根据数据创建分组
        if (!codeListGroup && !groupMap.has(groupFieldValue)) {
          groupMap.set(groupFieldValue, []);
        }
        if (groupMap.has(groupFieldValue)) {
          groupMap.get(groupFieldValue)!.push(item);
        }
      });
      this.state.groups = [];
      groupMap.forEach((value, key) => {
        const codeListItem = this.groupCodeListItems?.find(
          item => item.value === key,
        );
        this.state.groups.push({
          caption: codeListItem?.text || `${key}`,
          key,
          children: value,
        });
      });
    }
  }

  /**
   * 加载远程聚合数据
   *
   * @protected
   * @return {*}  {Promise<void>}
   */
  protected async loadRemoteAgg(): Promise<void> {
    const { aggMode, aggAppDataEntityId, aggAppDEDataSetId } = this.model;
    if (aggMode !== 'ALL') {
      return;
    }
    if (!aggAppDEDataSetId || !aggAppDataEntityId) {
      throw new RuntimeModelError(
        this.model,
        ibiz.i18n.t('runtime.controller.control.grid.missingConfiguration'),
      );
    }
    const params = await this.getFetchParams();
    delete params.sort;
    const app = ibiz.hub.getApp(this.context.srfappid);
    const res = (await app.deService.exec(
      aggAppDataEntityId,
      aggAppDEDataSetId,
      this.context,
      undefined,
      params,
    )) as HttpResponse<IData[]>;

    if (res.data.length) {
      [this.state.remoteAggResult] = res.data;
    }
  }

  /**
   * 计算当前页的聚合数据
   * @author lxm
   * @date 2023-08-07 04:22:09
   * @param {IData[]} items
   */
  calcAggResult(items: IData[]): void {
    Object.values(this.fieldColumns).forEach(column => {
      const result = column.calcFieldAgg(items);
      if (result) {
        this.state.aggResult[column.model.id!] = result;
      }
    });
  }

  /**
   * 后台删除结束后界面删除逻辑
   *
   * @param data
   */
  afterRemove(data: IData): void {
    super.afterRemove(data);
    // 删除this.state.rows里的表格行数据
    const index = this.findRowStateIndex(data);
    this.state.rows.splice(index, 1);

    // 删除分组里的那条数据
    this.state.groups.forEach(item => {
      if (item.children.length) {
        const findIndex = item.children.findIndex(
          item2 => item2.srfkey === data.srfkey,
        );
        if (findIndex !== -1) {
          item.children.splice(findIndex, 1);
        }
      }
    });
  }

  /**
   * 新建行
   *
   * @returns
   */
  async newRow(): Promise<void> {
    const { enableRowEdit, enableRowNew } = this.model;
    if (!(enableRowEdit && enableRowNew)) {
      ibiz.log.error(ibiz.i18n.t('runtime.controller.control.grid.newRows'));
      return;
    }

    const queryParams = { ...this.params };
    // 新建默认值
    const defaultData = this.calcDefaultValue({}, true);
    Object.assign(queryParams, defaultData);
    let res;
    try {
      res = await this.service.getDraft(this.context, queryParams);
    } catch (error) {
      this.actionNotification('GETDRAFTERROR', {
        error: error as Error,
      });
      throw error;
    }
    const draftData = res.data;
    // 处理后台导致的新建默认值丢失
    mergeDefaultInLeft(draftData, defaultData);
    // 加载完后续处理
    this.state.items.unshift(draftData);
    const newRow = new GridRowState(draftData, this);
    // 创建前端模拟主键，防止工具栏无法删除新建行
    const tempKey = createUUID();
    newRow.data.srfkey = tempKey;
    newRow.oldData.srfkey = tempKey;
    this.state.rows.unshift(newRow);
    this.gridStateNotify(newRow, GridNotifyState.DRAFT);
    this.actionNotification('GETDRAFTSUCCESS', { data: draftData });
  }

  /**
   * 保存
   *
   * @param data
   * @returns
   */
  async save(data: ControlVO): Promise<void> {
    if (this.state.isSimple) return;
    const isCreate = data.srfuf === Srfuf.CREATE;

    const rowState = this.findRowState(data);

    if (!rowState) {
      throw new RuntimeError(
        ibiz.i18n.t('runtime.controller.common.md.rowData'),
      );
    }

    // 更新行必须数据发变更才保存，新建行可以保存空
    if (!rowState.modified && !isCreate) {
      ibiz.log.debug(ibiz.i18n.t('runtime.controller.common.md.noChange'));
      return;
    }

    // 如果数据正在处理中，则延迟保存
    if (rowState.processing) {
      await awaitTimeout(500, this.save.bind(this), [data]);
      return;
    }

    const isValid = await this.validate(rowState);
    if (!isValid) {
      throw new RuntimeError(
        ibiz.i18n.t('runtime.controller.control.grid.saveCancel'),
      );
    }
    if (this.model.enableRowEditChangedOnly) data = rowState.getDiffData();
    // 如果为新建行，克隆数据重置主键，防止前端模拟主键污染数据
    if (isCreate) {
      data = data.clone();
      data.srfkey = undefined;
    }
    let res;
    const deName = calcDeCodeNameById(this.model.appDataEntityId!);
    const tempContext = this.context.clone();
    tempContext[deName] = data.srfkey;
    try {
      res = isCreate
        ? await this.service.create(tempContext, data)
        : await this.service.update(tempContext, data);
    } catch (error) {
      await this._evt.emit('onSaveError', undefined);
      this.actionNotification(`${isCreate ? 'CREATE' : 'UPDATE'}ERROR`, {
        error: error as Error,
        data: rowState.data,
        rowState,
      });
      throw error;
    }
    // 后续处理
    const index = this.findRowStateIndex(data);
    this.state.items.splice(index, 1, res.data);
    rowState.data = res.data;
    // 保存结束后更新旧数据
    rowState.oldData = res.data.clone();
    rowState.modified = false;

    this.gridStateNotify(rowState, GridNotifyState.SAVE);
    await this.updateRows(this.state.rows);
    await this._evt.emit('onSaveSuccess', undefined);

    // 发送实体创建或更新事件
    this.emitDEDataChange(isCreate ? 'create' : 'update', res.data);
  }

  /**
   * 保存表格所有数据
   * - 保存变更行和新建行
   * @return {*}  {Promise<void>}
   * @memberof GridController
   */
  async saveAll(): Promise<void> {
    const needSaveData = this.state.rows
      .filter(row => row.modified || row.data.srfuf === Srfuf.CREATE)
      .map(row => row.data);
    if (!needSaveData.length) return;
    await handleAllSettled(
      needSaveData.map(data => {
        return this.save(data);
      }),
    );
  }

  /**
   * 获取所有变更数据
   *
   * @returns
   */
  getAllModifiedData(): ControlVO[] {
    return this.state.rows.filter(row => row.modified).map(row => row.data);
  }

  /**
   * 获取所有新建数据
   *
   * @returns
   */
  getAllCreateData(): ControlVO[] {
    return this.state.rows
      .filter(row => row.data.srfuf === Srfuf.CREATE)
      .map(row => row.data);
  }

  /**
   * 初始化表格列控制器
   *
   * @protected
   */
  protected async initColumnsController(column: IDEGridColumn): Promise<void> {
    // 初始化适配器
    const provider = await getGridColumnProvider(column, this.model);
    if (!provider) {
      return;
    }
    this.providers[column.codeName!] = provider;

    // 初始化表格列控制器
    const controller = await provider.createController(column, this);
    this.columns[column.codeName!] = controller as GridColumnController;
    // 分类存放控制器
    if (
      column.columnType === 'DEFGRIDCOLUMN' ||
      column.columnType === 'DEFTREEGRIDCOLUMN'
    ) {
      this.fieldColumns[column.codeName!] =
        controller as GridFieldColumnController;
      if (column.enableRowEdit) {
        this.editColumns[column.codeName!] =
          controller as GridFieldEditColumnController;
      }
    } else if (column.columnType === 'UAGRIDCOLUMN') {
      this.uaColumns[column.codeName!] = controller as GridUAColumnController;
    } else if (column.columnType === 'GROUPGRIDCOLUMN') {
      // 有分组列模型说明就是有多级表头
      this.isMultistageHeader = true;
      await Promise.all(
        (column as IDEGridGroupColumn).degridColumns?.map(async childColumn => {
          await this.initColumnsController(childColumn);
        }) || [],
      );
    }
  }

  /**
   * @description 重置表格列状态
   * @returns {*} {void}
   */
  resetColumnStates(): void {
    this.state.columnStates = this.recursiveColumn(this.model.degridColumns || []);
    this.state.columnModelCach = false;
    this.configService.reset();
  }

  /**
   * @description 递归处理表格列状态
   * @param {IDEGridColumn[]} _columns
   * @returns {*} {IColumnExState[]}
   */
  protected recursiveColumn(
    _columns: IDEGridColumn[],
    callback?: (_column: IColumnExState) => IColumnExState,
  ): IColumnExState[] {
    const columns = _columns.filter(
      column => !(column.hiddenDataItem || column.hideMode === 2),
    );
    const columnStates: IColumnExState[] = [];
    columns.forEach(column => {
      let columnState: IColumnExState = {
        key: column.codeName!,
        caption: column.caption!,
        hidden: !!column.hideDefault,
        hideMode: column.hideMode || 0,
        uaColumn: column.columnType === 'UAGRIDCOLUMN',
        adaptive: column.widthUnit === 'STAR',
        columnWidth: column.width,
        columnType: column.columnType!,
      };
      if (callback) {
        columnState = callback(columnState);
      }
      if (column.columnType === 'GROUPGRIDCOLUMN')
        columnState.children = this.recursiveColumn(
          (column as IDEGridGroupColumn).degridColumns || [],
          callback,
        );
      columnStates.push(columnState);
    });
    return columnStates;
  }

  /**
   * 初始化表格列状态
   * @protected
   * @return {*}  {Promise<void>}
   */
  protected async initColumnStates(): Promise<void> {
    this.state.columnStates = [];
    const recursiveColumn = (_columns: IDEGridColumn[]): IColumnExState[] => {
      const columns = _columns.filter(
        column => !(column.hiddenDataItem || column.hideMode === 2),
      );
      const columnStates: IColumnExState[] = [];
      columns.forEach(column => {
        const columnState: IColumnExState = {
          key: column.codeName!,
          caption: column.caption!,
          hidden: !!column.hideDefault,
          hideMode: column.hideMode || 0,
          uaColumn: column.columnType === 'UAGRIDCOLUMN',
          adaptive: column.widthUnit === 'STAR',
          columnWidth: column.width,
          columnType: column.columnType!,
        };
        if (column.columnType === 'GROUPGRIDCOLUMN')
          columnState.children = recursiveColumn(
            (column as IDEGridGroupColumn).degridColumns || [],
          );
        columnStates.push(columnState);
      });
      return columnStates;
    };
    this.state.columnStates = recursiveColumn(this.model.degridColumns || []);
    const result = await this.configService.load();
    if (result && result.model) {
      this.state.columnModelCach = true;
      this.state.columnStates = this.orderColumnState(JSON.parse(result.model));
    }
    this.calcColumnFixed();
  }

  /**
   * 列状态排序
   * @param caches
   * @returns
   */
  protected orderColumnState(caches: IColumnExState[]): IColumnExState[] {
    const columnStates: IColumnExState[] = [];
    const calcChildren = (cache: IColumnExState, column: IColumnExState) => {
      if (column.columnType === 'GROUPGRIDCOLUMN') {
        cache.children = cache.children?.filter(child =>
          column.children?.find(_child => _child.key === child.key),
        );
        cache.children?.forEach(child => {
          const find = column.children?.find(
            _child => _child.key === child.key,
          );
          if (find) {
            calcChildren(child, find);
          }
        });
        column.children?.forEach(child => {
          const item = cache.children?.find(_child => child.key === _child.key);
          if (!item) {
            if (!cache.children) {
              cache.children = [child];
            } else {
              cache.children.push(child);
            }
          }
        });
      }
    };

    // 根据缓存顺序填充
    caches.forEach(cache => {
      const column = this.state.columnStates.find(
        item => item.key === cache.key,
      );
      if (column) {
        calcChildren(cache, column);
        columnStates.push(cache);
      }
    });
    // 最后将未缓存的按模型顺序添加
    this.state.columnStates.forEach(column => {
      const item = caches.find(cache => column.key === cache.key);
      if (!item) columnStates.push(column);
    });
    return columnStates;
  }

  /**
   * 计算列的固定状态
   *
   */
  calcColumnFixed(): void {
    const showColumns = this.state.columnStates.filter(
      column => !column.hidden,
    );
    const allNum = showColumns.length;
    const { frozenFirstColumn, frozenLastColumn } = this.model;
    this.hasAdaptiveColumn = false;
    showColumns.forEach((column, index) => {
      if (column.adaptive) {
        this.hasAdaptiveColumn = true;
      }
      if (column.uaColumn) {
        column.fixed = index + 1 <= Math.floor(allNum / 2) ? 'left' : 'right';
      } else if (frozenFirstColumn && index < frozenFirstColumn) {
        column.fixed = 'left';
      } else if (frozenLastColumn && index >= allNum - frozenLastColumn) {
        column.fixed = 'right';
      }
    });
  }

  /**
   * 初始化表格列
   *
   * @protected
   */
  protected async initGridColumns(): Promise<void> {
    if (this.model.degridColumns) {
      await Promise.all(
        this.model.degridColumns.map(async column =>
          this.initColumnsController(column),
        ),
      );
    }
  }

  /**
   * 设置行属性的值
   *
   * @param row
   * @param name
   * @param value
   * @returns
   */
  async setRowValue(
    row: GridRowState,
    name: string,
    value: unknown,
  ): Promise<void> {
    if (
      Object.prototype.hasOwnProperty.call(row.data, name) &&
      !isValueChange(row.data[name], value)
    ) {
      // `表格行数据里没有属性${name}或者${name}的值未发生改变`
      return;
    }
    // 改变值
    row.data[name] = value;
    row.modified = true;
    row.processing = true;

    try {
      await this.dataChangeNotify(row, [name]);
      await this._evt.emit('onGridDataChange', {
        data: {
          name,
          index: this.findRowStateIndex(row.data),
          items: this.state.rows.map(_row => {
            return _row.data;
          }),
        },
      });
    } finally {
      row.processing = false;
    }
  }

  /**
   * 设置过滤值
   *
   * @param name 过滤项名称
   * @param value 值
   */
  setFilterValue(name: string, value: unknown): void {
    this.state.columnFilter[name] = value;
  }

  /**
   * 通知所有表格编辑项成员表格编辑项数据变更
   *
   * @param {GridRowState} row 行数据
   * @param {string[]} names 更新的属性
   */
  async dataChangeNotify(row: GridRowState, names: string[]): Promise<void> {
    // 通知所有编辑项去处理编辑项相关逻辑
    await handleAllSettled(
      Object.values(this.editColumns).map(async column => {
        return column.dataChangeNotify(row, names);
      }),
    );
  }

  /**
   *  表格状态变更通知
   * @param row
   * @param state
   */
  gridStateNotify(row: GridRowState, state: GridNotifyState): void {
    // 通知表格编辑列
    Object.values(this.editColumns).forEach(column => {
      column.gridStateNotify(row, state);
    });
  }

  /**
   *  校验一行数据的所有编辑项
   * @param row 要校验的行数据控制器
   * @returns
   */
  async validate(row: GridRowState): Promise<boolean> {
    const values = await Promise.all(
      Object.values(this.editColumns).map(column => column.validate(row)),
    );

    // 找不到value为false即全部是true
    return values.findIndex(value => !value) === -1;
  }

  /**
   * 校验所有编辑项
   *
   * @returns
   */
  async validateAll(): Promise<boolean> {
    const values = await Promise.all(
      Object.values(this.state.rows).map(row => this.validate(row)),
    );

    // 找不到value为false即全部是true
    return values.findIndex(value => !value) === -1;
  }

  /**
   * 切换表格的行编辑开启关闭状态
   *
   */
  async toggleRowEdit(): Promise<void> {
    if (!this.model.enableRowNew) {
      throw new RuntimeError(
        ibiz.i18n.t('runtime.controller.control.grid.noSupportRowEditing'),
      );
    }
    this.state.rowEditOpen = !this.state.rowEditOpen;
  }

  /**
   * 获取部件默认排序模型
   *
   * @returns
   */
  getSortModel(): {
    minorSortAppDEFieldId: string | undefined;
    minorSortDir: string | undefined;
  } {
    return {
      minorSortAppDEFieldId: this.model.minorSortAppDEFieldId,
      minorSortDir: this.model.minorSortDir,
    };
  }

  /**
   * 设置排序
   *
   * @param fieldId
   * @param order
   */
  setSort(fieldId?: string, order?: 'asc' | 'desc'): void {
    let fieldName;
    if (fieldId) {
      fieldName = this.fieldIdNameMap.get(fieldId)!.toLowerCase();
    }
    super.setSort(fieldName, order);
  }

  /**
   * 表格编辑项更新
   *
   * @param row
   * @param updateId
   */
  async updateGridEditItem(row: GridRowState, updateId: string): Promise<void> {
    const findUpdate = this.model.degridEditItemUpdates?.find(
      item => item.id === updateId,
    );

    if (!findUpdate) {
      throw new RuntimeError(
        ibiz.i18n.t('runtime.controller.control.grid.updateColumns', {
          updateId,
        }),
      );
    }

    const { appDEMethodId, degeiupdateDetails, customCode, scriptCode } =
      findUpdate;
    const updateItems = degeiupdateDetails!.map(item => item.id!);

    let updateData: IData; // 要修改的数据
    if (customCode && scriptCode) {
      // 脚本模式获取修改数据
      updateData = ScriptFactory.execScriptFn(
        {
          ...this.getEventArgs(),
          data: row.data,
        },
        scriptCode,
        {
          isAsync: false,
        },
      ) as IData;
    } else {
      // 后台服务获取修改数据
      const params = { ...this.params, ...row.data.getOrigin() };
      const res = await this.service.updateGridEditItem(
        appDEMethodId!,
        this.context,
        params,
      );
      updateData = res.data;
    }
    // 修改更新的值
    if (updateData && updateItems?.length) {
      await Promise.all(
        updateItems.map(itemName => {
          return this.setRowValue(row, itemName, updateData![itemName]);
        }),
      );
    }
  }

  /**
   * 加载数据(只加载数据 不做其他操作)
   *
   * @param args
   * @returns
   */
  async loadData(args: MDCtrlLoadParams): Promise<ControlVO[]> {
    // *查询参数处理
    const { context } = this.handlerAbilityParams(args);
    const params = await this.getFetchParams(args?.viewParam);
    let res;
    // *发起请求
    await this.startLoading();
    try {
      res = await this.service.fetch(context, params);
    } finally {
      await this.endLoading();
    }
    return res.data;
  }

  /**
   * 初始化数据导出列
   *
   * @param dataExport
   * @returns
   */
  async findAllExportColumns(
    dataExport: IDEDataExport,
  ): Promise<IExportColumn[]> {
    const app = ibiz.hub.getApp(this.context.srfappid);
    const exportColumnsPromises: Promise<IExportColumn>[] | undefined =
      dataExport.dedataExportItems?.map(async item => {
        const tempExportColumn: IExportColumn = { ...item };
        if (item.codeListId) {
          // 加载代码表模型
          tempExportColumn.codeList = app.codeList.getCodeList(item.codeListId);
          tempExportColumn.codeListItems = await app.codeList.get(
            item.codeListId,
            this.context,
          );
        }
        return tempExportColumn;
      });
    // 使用 Promise.all 等待所有 Promise 解析
    if (exportColumnsPromises) {
      return Promise.all(exportColumnsPromises);
    }
    return [];
  }

  /**
   * 值格式化
   *
   * @param isDate
   * @param valueFormat
   * @param value
   * @returns
   */
  formatValue = (
    isDate: boolean,
    valueFormat?: string,
    value: unknown = '',
  ): string => {
    // 根据格式化配置格式化显示
    const strVal = `${value}`;
    if (!valueFormat) {
      return strVal;
    }
    if (isDate && dayjs(strVal, valueFormat, true).isValid()) {
      const formatVal = dayjs(strVal).format(valueFormat);
      return formatVal;
    }
    return ibiz.util.text.format(strVal, valueFormat);
  };

  /**
   * 格式化导出数据
   *
   * @param data
   * @returns
   */
  formatExcelData(data: IData[]): IData[] {
    const { fields = [] } = this.dataExportParam;
    const cloneData = clone(
      data.map(item => {
        return fields.reduce((obj: IData, key: string) => {
          obj[key] = item[key];
          return obj;
        }, {});
      }),
    );
    cloneData.forEach(item => {
      Object.keys(item).forEach((key: string) => {
        let value = item[key];
        const fieldColumnC = this.fieldColumns[key];
        const format = fieldColumnC?.valueFormat;
        const curField = this.dataEntity.appDEFields?.find(
          _item => _item.id === key,
        );
        const isDate =
          (curField && DataTypes.isDate(curField.stdDataType!)) || false;
        const unitName = fieldColumnC?.model.unitName || '';
        if (fieldColumnC && fieldColumnC.model.valueType !== 'SIMPLE') {
          value = ValueExUtil.toText(fieldColumnC.model, value);
        }
        if (this.allExportCodelistMap.get(key)) {
          value =
            this.allExportCodelistMap.get(key)!.find(x => x.value === item[key])
              ?.text || value;
        } else {
          value = this.formatValue(isDate, format, value) + unitName;
        }
        item[key] = value;
      });
    });
    return cloneData;
  }

  /**
   * 获取导出数据
   *
   * @param params
   * @returns
   */
  async getExportData(params: IData): Promise<IData[]> {
    const { type } = params;
    let data: IData[] = [];
    // 未指定类型时，默认导出当前页
    if (!type || type === 'activatedPage') {
      data = this.state.rows.map(row => row.data);
    } else if (type === 'maxRowCount' || type === 'customPage') {
      const { size } = this.state;
      const { startPage, endPage } = params;
      const viewParam =
        type === 'customPage'
          ? {
              page: 0,
              offset: (startPage - 1) * size,
              size: (endPage - startPage + 1) * size,
            }
          : { size: this.dataExport?.maxRowCount || 1000, page: 0 };
      data = await this.loadData({ viewParam });
    } else if (type === 'selectedRows') {
      data = this.getData();
    }
    if (data.length === 0) {
      throw new RuntimeError(
        ibiz.i18n.t('runtime.controller.control.grid.exported'),
      );
    }
    return data;
  }

  /**
   * 数据导出
   * @param args
   */
  async exportData(args: { event: MouseEvent; params: IData }): Promise<void> {
    const { header } = this.dataExportParam;
    if (!header) {
      throw new RuntimeError(
        ibiz.i18n.t('runtime.controller.control.grid.tabularColumns'),
      );
    }
    // 导出前先填充导出代码表数据
    await this.fillExportCodelistMap();
    const data = await this.getExportData(args.params);
    const formatData = this.formatExcelData(data);
    const table = formatData.map(v => Object.values(v));
    await exportData(header, table, this.model.logicName!);
  }

  /**
   * 计算默认值并返回一个对象，对象里的属性就是要填充的默认值
   * - 没有的属性就是不需要填充默认值的属性
   * @param data
   * @param isCreate
   * @returns
   */
  calcDefaultValue(data: IData, isCreate: boolean): IData {
    const result: IData = {};
    Object.values(this.editColumns).forEach(c => {
      const { createDV, createDVT, updateDV, updateDVT } = c.editItem;
      const valueType = isCreate ? createDVT : updateDVT;
      const defaultValue = isCreate ? createDV : updateDV;
      const defaultVal = getDefaultValue(
        {
          name: c.fieldName,
          valueType,
          defaultValue,
          valueFormat: c.valueFormat,
        },
        { data, context: this.context, params: this.params },
      );
      if (defaultVal !== undefined) {
        result[c.fieldName] = defaultVal;
      }
    });
    return result;
  }

  /**
   * 查找rowState所在index
   * @param data
   * @returns
   */
  findRowStateIndex(data: IData): number {
    const isCreate = data.srfuf === Srfuf.CREATE;
    const compareKey = isCreate ? 'tempsrfkey' : 'srfkey';
    return this.state.rows.findIndex(
      item => item.data[compareKey] === data[compareKey],
    );
  }

  /**
   * 查找rowState
   *
   * @param data
   * @returns
   */
  findRowState(data: IData): IGridRowState | undefined {
    const index = this.findRowStateIndex(data);
    return index !== -1 ? this.state.rows[index] : undefined;
  }

  /**
   * 行单击事件
   *
   * @param data
   */
  async onRowClick(data: IData): Promise<void> {
    // 表格行单击时，只选中点击行
    this.setSelection([data]);
    // 设置导航数据
    this.setNavData(data);
    // 激活事件
    if (this.state.mdctrlActiveMode === 1) {
      await this.setActive(data);
    }
    this._evt.emit('onRowClick', { data: [data] });
  }

  /**
   * 行双击事件
   *
   * @param data
   */
  async onDbRowClick(_data: IData): Promise<void> {
    const data = this.state.items.find(item => item.srfkey === _data.srfkey);
    if (this.state.mdctrlActiveMode === 2 && data) {
      await this.setActive(data);
    }
    this._evt.emit('onDbRowClick', { data: [_data] });
  }

  /**
   * 转换各类多语言
   *
   */
  protected convertMultipleLanguages(): void {
    const convertColumnCaption = (columns: IDEGridColumn[]): void => {
      columns.forEach((column: IDEGridColumn) => {
        if (column.capLanguageRes && column.capLanguageRes.lanResTag) {
          column.caption = ibiz.i18n.t(
            column.capLanguageRes.lanResTag,
            column.caption,
          );
        }
        if (
          column.columnType === 'GROUPGRIDCOLUMN' &&
          (column as IDEGridGroupColumn).degridColumns
        ) {
          convertColumnCaption((column as IDEGridGroupColumn).degridColumns!);
        }
      });
    };
    if (this.model.degridColumns && this.model.degridColumns.length > 0) {
      convertColumnCaption(this.model.degridColumns);
    }
  }

  /**
   * 控制列显示
   *
   * @param columnState
   * @returns
   */
  setColumnVisible(columnState: IColumnExState): void {
    if (columnState.hideMode === 3) {
      return;
    }
    columnState.hidden = !columnState.hidden;
    this.calcColumnFixed();
    this.saveColumnStates();
  }

  /**
   * 设置点击分组后回显相关参数
   *
   * @param data
   */
  setGroupParams(data: ISearchGroupData): void {
    if (data.sort) {
      this.isSetSort = true;
      this.state.sortQuery = data.sort;
    } else {
      this.state.sortQuery = '';
    }
    this.initColumnStates();
  }

  /**
   * 改变列排序
   * @param columnKey
   * @param newIndex
   * @param oldIndex
   */
  changeColumnStateSort(
    columnKey: string,
    newIndex: number,
    oldIndex: number,
  ): void {
    const columnState = this.state.columnStates.find(
      item => item.key === columnKey,
    );
    if (columnState) {
      // 移除元素从旧位置，将元素插入到新位置
      this.state.columnStates.splice(oldIndex, 1);
      this.state.columnStates.splice(newIndex, 0, columnState);
      this.calcColumnFixed();
      this.saveColumnStates();
    }
  }

  /**
   * 存储列状态
   *
   */
  async saveColumnStates(): Promise<void> {
    const data: IData = {
      model: JSON.stringify(this.state.columnStates),
    };
    this.state.columnModelCach = true;
    await this.configService.save(data);
  }

  /**
   * 执行对应部件行为消息提示
   *
   * @param tag
   * @param opts
   */
  actionNotification(
    tag: string,
    opts?: {
      default?: string;
      data?: IData | IData[];
      error?: Error;
      rowState?: IGridRowState;
    },
  ): void {
    if (opts?.error && opts?.rowState && opts.error instanceof EntityError) {
      const { details, message } = opts.error;
      const { errors, data, oldData } = opts.rowState;
      if (this.saveErrorHandleMode === 'reset') {
        if (data && Object.keys(data).length > 0) {
          Object.keys(data).forEach(key => {
            if (data[key] !== oldData[key]) {
              data[key] = oldData[key];
            }
          });
        }
      } else {
        details.forEach(detail => {
          errors[detail.name] = detail.errorInfo || message;
        });
      }
    }
    super.actionNotification(tag, { ...(opts || {}) });
  }

  /**
   * 切换行展开
   *
   * @param params
   */
  changeCollapse(params: IData = {}): void {
    const { tag, expand } = params;
    // 存在分组id则展开/收缩分组
    if (tag) {
      const row = this.state.items.find(x => x.srfkey === tag);
      if (row) {
        this._evt.emit('onToggleRowExpansion', { row, expand });
      }
    } else {
      // 不存在分组id时全展开/全收缩
      const { groups } = this.state;
      groups.forEach(group => {
        if (group.children && group.children.length > 0) {
          const row = group.children[0];
          this._evt.emit('onToggleRowExpansion', { row, expand });
        }
      });
    }
  }

  /**
   * @description 获取表格列
   * @template K
   * @param {K} type 表格列类型
   * @param {string} id 表格列标识
   * @returns {*}  {IApiGridColumnMapping[K]}
   * @memberof IApiGridController
   */
  getGridColumn<K extends keyof IApiGridColumnMapping>(
    type: K,
    id: string,
  ): IApiGridColumnMapping[K] {
    return this.columns[id] as unknown as IApiGridColumnMapping[K];
  }
}
